package br.ufrj.dcc.tp.servidor.model;

import java.awt.Component;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

import br.ufrj.dcc.tp.servidor.gui.Window;
import br.ufrj.dcc.tp.servidor.interfaces.IServerController;
import br.ufrj.dcc.tp.servidor.log.ConsoleLogStream;
import br.ufrj.dcc.tp.servidor.log.Log;
import br.ufrj.dcc.tp.servidor.log.LogLevel;
import br.ufrj.dcc.tp.utils.Config;
import br.ufrj.dcc.tp.utils.SocketAction;
import br.ufrj.dcc.tp.utils.SocketMessageImpl;

public class Server extends Thread implements IServerController {
	
	public static final String SERVER_NAME = "Server";
	private Notifier notifier;
	private int port;
	private ServerSocket socketServer = null;
	private Window window;
	
	public Server( int port ) throws IOException {
		this.port = port;
		
		iniciaInterface();
		
		Log.addLogStream(window);
		Log.addLogStream(new ConsoleLogStream());
		
		Log.write(this.getClass(), LogLevel.INFO, "Inicializando o ServerSocket.");
		socketServer = new ServerSocket(this.port);
		// Dando um timeout para a escuta por conexoes
		// Com isso, o servidor pode escutar interrupcoes da thread e trata-la.
		socketServer.setSoTimeout(Config.IO_READ_TIMEOUT.toInteger());
		Log.write(this.getClass(), LogLevel.INFO, "Inicializando o Notificador de clientes.");
		notifier = new Notifier();
		Log.write(this.getClass(), LogLevel.INFO, "Servidor escutando porta  " + port + ".");
		// Start o notificador de eventos
		notifier.start();
	}
	
	public Server() throws IOException {
		this(Config.DEFAULT_SERVER_PORT.toInteger());
	}
	
	/** Inicia uma nova interface para o servidor. */
	private void iniciaInterface(){
		this.window = new Window(this);
	}
	
	@Override
	public void run() { 
		try {
			while( ! isInterrupted() ) {
				try {
					
					Socket socket = socketServer.accept();
					Log.write(this.getClass(), LogLevel.INFO,
								"Conexao de " + socket.getInetAddress().getHostAddress() + ":" + socket.getPort()
								+ " foi aceita.");
					
					new SocketWorker(socket, notifier).start();
				} catch( SocketTimeoutException e ) {
				} catch (IOException e) {
					Log.write(this.getClass(), e);
				}
				sleep(Config.SLEEP_TIMEOUT.toInteger());
			}
		} catch (InterruptedException e) {
		} finally {
			try {
				Log.write(this.getClass(), LogLevel.INFO, "Servidor parando...");
				notifier.unsubscribeAll();
				Log.write(this.getClass(), LogLevel.INFO,"Clientes finalizados.");
			} catch (Exception e) {
				Log.write(this.getClass(), LogLevel.SEVERE,"Erro ao finalizar clientes.");
				Log.write(this.getClass(), e);
			}
			
			notifier.interrupt();
			window.silentDispose();
			
			try {
				Log.write(this.getClass(), LogLevel.INFO, "Finalizando SocketServer...");
				socketServer.close();
				Log.write(this.getClass(), LogLevel.INFO, "SocketServer finalizado...");
			} catch (IOException e) {
				Log.write(this.getClass(), LogLevel.SEVERE, "Erro ao finalizar SocketServer.");
				Log.write(this.getClass(), e);
			}
			
		}
	}
	
	@Override
	public void shutdown() {
		this.interrupt();
	}
	
	@Override
	public void sendMessage(String message) {
		SocketMessageImpl socketMessage = new SocketMessageImpl("Server", SocketAction.SEND_MESSAGE, null, message);
		notifier.update(socketMessage);
	}
	
	@Override
	public boolean hasActiveClients() {
		if(notifier == null) return false;
		return (notifier.getNumClients() > 0) ? true : false;
	}
	
	public Component getComponent(Class<? extends Component> klass ) {
		if( klass.isInstance(window) ) {
			return window;
		}
		return null;
	}
}
